import type { FormItemRule, ColProps, FormItemProps } from "element-plus";

export type OnlyOneKey<T extends Record<string, any>> = {
  [K in keyof T]: {
    [P in K]?: T[P];
  } & Partial<Record<Exclude<keyof T, K>, never>>;
}[keyof T];

/** 额外扩展表单配置项 */
export type FormItemConfigExtra = OnlyOneKey<{
  /** 是输入类型 */
  isInput: boolean;
  /** 是选择类型 */
  isSelect: boolean;
}> & {
  /** 回车提交 */
  enterSubmit?: boolean;
  /** 失去焦点提交[isInput默认为true] */
  blurSubmit?: boolean;
  /** 变化提交 [isSelect默认为true] */
  changeSubmit?: boolean;
};

interface WrapProps extends FormItemProps {
  [key: string]: any;
}

/** formItem联动配置 */
interface FormItemLinkConfig<K extends string, BV, OV> {
  /** 关联(该宿主配置)表单项的key */
  key: K;
  /**
   * 获取该项的值
   * @param hostValue 宿主/依赖项的值
   * @param ownValue 该(寄生/收集依赖)项的值
   * @param attachInfo 附加信息，可用于表单渲染为自定义组件时触发事件triggerChangeLink, 此场景 寄生项的值内部会去拿
   * @example emit('triggerChangeLink', hostValue, attachInfo)
   */
  getValue: (hostValue: BV, ownValue: OV, attachInfo?: any) => void;
}

/** formItem配置 */
export interface FormItemConfig<
  PO extends Record<string, any> = Record<string, any>,
  /** 需要格式化的模型 */
  SO extends Record<string, any> = Record<string, any>,
  /** 解析的模型的key */
  PK extends Extract<keyof PO, string> = Extract<keyof PO, string>,
  /** 解析的模型的值 */
  PV extends PO[PK] = PO[PK],
  /** 需要格式化的模型的值 */
  SV extends PK extends keyof SO ? SO[PK] : PV = PK extends keyof SO
    ? SO[PK]
    : PV,
> {
  /** 表单项key __大写视为忽略 等价于 ignore = true */
  key: PK;
  /** 表单项label */
  label: string;
  /** 表单布局 优先级高于FormMain同名属性 */
  layout?: Partial<ColProps>;
  /** 表单外FormItem属性 */
  wrapProps?:
    | Partial<WrapProps>
    | ((scope: FormScope<PO, SO, PK, PV, SV>) => Partial<WrapProps>);
  /** 表单主体属性 */
  props?:
    | Record<string, any>
    | ((scope: FormScope<PO, SO, PK, PV, SV>) => Record<string, any>);
  /** 表单渲染函数/渲染标签/组件 */
  render?:
    | string
    | Record<string, any>
    | ((
        props: {
          modelValue: PV;
          ["onUpdate:modelValue"]: (value: PV) => void;
          placeholder?: string;
          ["onTriggerChangeLink"]?: (value: SV, attachInfo?: any) => void;
        } & Record<string, any>,
      ) => VNode);
  /** 表单校验规则 */
  rules?:
    | FormItemRule[]
    | ((scope: FormScope<PO, SO, PK, PV, SV>) => FormItemRule[]);
  /** 表单初始值 */
  init?: (() => PV) | PV;
  /** 表单回显解析处理 */
  parse?: (
    value: any,
    parseData: Partial<PO>,
    originData: ExtractFormStringifyFromObject<PO, SO>,
  ) => PV | undefined;
  /** 表单序列化处理 */
  stringify?: (
    value: any,
    stringifyData: Partial<ExtractFormStringifyFromObject<PO, SO>>,
    originData: PO,
  ) => SV | undefined;
  /**
   * 是否在初始化数据、解析数据、序列化数据时忽略处理
   */
  ignore?: boolean;
  /**
   * 是否在表单中隐藏
   */
  hide?:
    | boolean
    | ((
        scope: FormScope<PO, SO, PK, PV, SV> & {
          getInitValue: () => PV;
        },
      ) => boolean);
  /**
   * 被关联配置
   * 1. 不要配置当前表单项的key
   * 2. 最好不要循环关联
   * 3. 必须循环关联时，避免循环引用
   */
  beLink?: FormItemLinkConfig<PK, PV, any> | FormItemLinkConfig<PK, PV, any>[];
  /**
   * 额外一些配置
   */
  extra?: FormItemConfigExtra;
}

/** form配置 */
export type FormItemConfigList<
  PO extends Record<string, any> = Record<string, any>,
  SO extends Record<string, any> = Record<string, any>,
> = FormItemConfig<PO, SO>[];

/** 表单数据 */ export type FormModel<L> =
  L extends FormItemConfigList<infer PO, any> ? PO : Record<string, any>;

/** 提取格式化原始对象 */
export type ExtractFormStringifyRawFromObject<
  PO extends Record<string, any>,
  SO extends Record<string, any>,
> = {
  [K in keyof PO | keyof SO]: K extends keyof SO
    ? SO[K] extends never
      ? never
      : SO[K] extends undefined
        ? K extends keyof PO
          ? PO[K]
          : never
        : SO[K]
    : K extends keyof PO
      ? PO[K]
      : never;
};

/** 表单序列化数据原始 */
export type ExtractFormStringifyRawFromList<L> =
  L extends FormItemConfigList<infer PO, infer SO>
    ? PO extends Record<string, any>
      ? SO extends Record<string, any>
        ? ExtractFormStringifyFromObject<PO, SO>
        : Record<string, any>
      : Record<string, any>
    : Record<string, any>;

/** 提取除never以外的属性值 */
export type ExcludeNeverProperties<T> = {
  [K in keyof T as T[K] extends never ? never : K]: T[K];
};

export type ExtractFormStringifyFromObject<
  PO extends Record<string, any>,
  SO extends Record<string, any>,
> = ExcludeNeverProperties<ExtractFormStringifyRawFromObject<PO, SO>>;

/** 表单序列化数据 */
export type ExtractFormStringifyFromList<L> = ExcludeNeverProperties<
  ExtractFormStringifyRawFromList<L>
>;

/** form作用域数据 */
export interface FormScope<
  /** 解析的模型 */
  PO extends Record<string, any>,
  /** 需要格式化的模型 */
  SO extends Record<string, any>,
  /** 解析的模型的key */
  PK extends Extract<keyof PO, string>,
  /** 解析的模型的值 */
  PV extends PO[PK],
  /** 需要格式化的模型的值 */
  SV extends PK extends keyof SO ? SO[PK] : PV,
> {
  data: PO;
  config: FormItemConfig<PO, SO, PK, PV, SV>;
}

/** FormMain expose类型 */
export interface FormMainInstance {
  validate: () => Promise<void>;
  resetFields: () => void;
  clearValidate: (key?: string) => void;
}

/** FormSearch expose类型 */
export interface FormSearchInstance extends FormMainInstance {
  /** 触发搜索 */
  triggerSearch: () => void;
  /** 触发重置 */
  triggerReset: () => void;
}

/** FormItem props */
export interface FormMainItemProps {
  config: FormItemConfig<Record<string, any>, Record<string, any>>;
  data: Record<string, any>;
}

/** FormMain props */
export interface FormMainProps<
  PO extends Record<string, any>,
  SO extends Record<string, any>,
> {
  list: FormItemConfigList<PO, SO>;
  data: PO;
  layout?: Partial<ColProps>;
}

/** FormSearch props */
export interface FormSearchProps<
  PO extends Record<string, any>,
  SO extends Record<string, any>,
  SQ extends Record<string, any>,
> extends FormMainProps<PO, SO> {
  labelPosition?: "right" | "left" | "top";
  labelWidth?: string;
  /** 静态参数(非表单 非分页参数) */
  staticQuery?: SQ;
}

/** form item loading 信息 */
export interface FormItemLoadingInfo {
  key: string;
  label: string;
  message: string;
}

/** form item 加载状态信息 */
export type FormItemLoadingChangeFunc = (data: {
  key: string;
  message?: string;
}) => void;

/** form tree 单项类型 */
export interface FormTreeItem {
  id: string | number;
  label: string;
  children?: FormTreeItem[];
  disabled?: boolean;
  [key: string]: any;
}

/** form单项表单提交类型 */
export type FormItemSubmitType = "blur" | "change" | "enter";

/** 表单搜索搜索类型 */
export type FormSearchSearchType = "search" | "reset" | FormItemSubmitType;
